package com.shipwe.loans.utils.imgcovert

import com.shipwe.loans.utils.log.ShipweLogger
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.IOException
import java.io.InputStream

object ImageChecker {

    private val format = listOf(".jpg", ".jpeg", ".png", ".webp", ".gif")

    private val JPEG_SIGNATURE = byteArrayOf(0xFF.toByte(), 0xD8.toByte(), 0xFF.toByte())

    fun isJPG(source: InputStream): Boolean {
        return isJPG(toByteArray(source))
    }

    fun getOrientation(source: InputStream): Int {
        return getOrientation(toByteArray(source))
    }

    private fun isJPG(data: ByteArray?): Boolean {
        if (data == null || data.size < 3) {
            return false
        }
        val signatureB = byteArrayOf(data[0], data[1], data[2])
        return JPEG_SIGNATURE.contentEquals(signatureB)
    }

    private fun getOrientation(jpeg: ByteArray?): Int {
        if (jpeg == null) {
            return 0
        }

        var offset = 0
        var length = 0

        while (offset + 3 < jpeg.size && (jpeg[offset++].toInt() and 0xFF) == 0xFF) {
            val marker = jpeg[offset].toInt() and 0xFF

            if (marker == 0xFF) {
                continue
            }
            offset++

            if (marker == 0xD8 || marker == 0x01) {
                continue
            }
            if (marker == 0xD9 || marker == 0xDA) {
                break
            }

            length = pack(jpeg, offset, 2, false)
            if (length < 2 || offset + length > jpeg.size) {
                ShipweLogger.i("Invalid length")
                return 0
            }

            if (marker == 0xE1 && length >= 8
                && pack(jpeg, offset + 2, 4, false) == 0x45786966
                && pack(jpeg, offset + 6, 2, false) == 0) {
                offset += 8
                length -= 8
                break
            }

            offset += length
            length = 0
        }

        if (length > 8) {
            val tag = pack(jpeg, offset, 4, false)
            if (tag != 0x49492A00 && tag != 0x4D4D002A) {
                ShipweLogger.i("Invalid byte order")
                return 0
            }
            val littleEndian = (tag == 0x49492A00)

            var count = pack(jpeg, offset + 4, 4, littleEndian) + 2
            if (count < 10 || count > length) {
                ShipweLogger.i("Invalid offset")
                return 0
            }
            offset += count
            length -= count

            count = pack(jpeg, offset - 2, 2, littleEndian)
            while (count-- > 0 && length >= 12) {
                val mark = pack(jpeg, offset, 2, littleEndian)
                if (mark == 0x0112) {
                    val orientation = pack(jpeg, offset + 8, 2, littleEndian)
                    when (orientation) {
                        1 -> return 0
                        3 -> return 180
                        6 -> return 90
                        8 -> return 270
                    }
                    ShipweLogger.i("Unsupported orientation")
                    return 0
                }
                offset += 12
                length -= 12
            }
        }

        ShipweLogger.i("Orientation not found")
        return 0
    }

    fun needCompress(leastCompressSize: Int, path: String): Boolean {
        return if (leastCompressSize > 0) {
            val source = File(path)
            source.exists() && source.length() > (leastCompressSize shl 10)
        } else true
    }

    private fun pack(bytes: ByteArray, offset: Int, length: Int, littleEndian: Boolean): Int {
        var step = 1
        var currentOffset = offset
        if (littleEndian) {
            currentOffset += length - 1
            step = -1
        }

        var value = 0
        var currentLength = length
        while (currentLength-- > 0) {
            value = (value shl 8) or (bytes[currentOffset].toInt() and 0xFF)
            currentOffset += step
        }
        return value
    }

    private fun toByteArray(source: InputStream?): ByteArray {
        if (source == null) {
            return byteArrayOf()
        }

        val buffer = ByteArrayOutputStream()

        var read: Int
        val data = ByteArray(4096)

        try {
            while (source.read(data, 0, data.size).also { read = it } != -1) {
                buffer.write(data, 0, read)
            }
        } catch (ignored: Exception) {
            return byteArrayOf()
        } finally {
            try {
                buffer.close()
            } catch (ignored: IOException) {
            }
        }

        return buffer.toByteArray()
    }
}
